{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-02T10:50:31.982740Z",
     "start_time": "2020-01-02T10:50:31.976911Z"
    }
   },
   "source": [
    "<style>\n",
    "pre {\n",
    " white-space: pre-wrap !important;\n",
    "}\n",
    ".table-striped > tbody > tr:nth-of-type(odd) {\n",
    "    background-color: #f9f9f9;\n",
    "}\n",
    ".table-striped > tbody > tr:nth-of-type(even) {\n",
    "    background-color: white;\n",
    "}\n",
    ".table-striped td, .table-striped th, .table-striped tr {\n",
    "    border: 1px solid black;\n",
    "    border-collapse: collapse;\n",
    "    margin: 1em 2em;\n",
    "}\n",
    ".rendered_html td, .rendered_html th {\n",
    "    text-align: left;\n",
    "    vertical-align: middle;\n",
    "    padding: 4px;\n",
    "}\n",
    "</style>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Machine Learning with vaex.ml\n",
    "\n",
    "If you want to try out this notebook with a live Python kernel, use mybinder:\n",
    "\n",
    "<a class=\"reference external image-reference\" href=\"https://mybinder.org/v2/gh/vaexio/vaex/latest?filepath=docs%2Fsource%2Ftutorial_ml.ipynb\"><img alt=\"https://mybinder.org/badge_logo.svg\" src=\"https://mybinder.org/badge_logo.svg\" width=\"150px\"></a>\n",
    "\n",
    "\n",
    "The `vaex.ml` package brings some machine learning algorithms to `vaex`. If you installed the individual subpackages (`vaex-core`, `vaex-hdf5`, ...) instead of the `vaex` metapackage, you may need to install it by running `pip install vaex-ml`, or `conda install -c conda-forge vaex-ml`.\n",
    "\n",
    "The API of `vaex.ml` stays close to that of [scikit-learn](https://scikit-learn.org/stable/), while providing better performance and the ability to efficiently perform operations on data that is larger than the available RAM. This page is an overview and a brief introduction to the capabilities offered by `vaex.ml`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T13:51:40.577402Z",
     "start_time": "2020-06-04T13:51:39.920409Z"
    }
   },
   "outputs": [],
   "source": [
    "import vaex\n",
    "vaex.multithreading.thread_count_default = 8\n",
    "import vaex.ml\n",
    "\n",
    "import numpy as np\n",
    "import pylab as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will use the well known [Iris flower](https://en.wikipedia.org/wiki/Iris_flower_data_set) and Titanic passenger list datasets, two classical datasets for machine learning demonstrations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T13:51:40.593353Z",
     "start_time": "2020-06-04T13:51:40.578388Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_\n",
       "0    5.9             3.0            4.2             1.5            1\n",
       "1    6.1             3.0            4.6             1.4            1\n",
       "2    6.6             2.9            4.6             1.3            1\n",
       "3    6.7             3.3            5.7             2.1            2\n",
       "4    5.5             4.2            1.4             0.2            0\n",
       "...  ...             ...            ...             ...            ...\n",
       "145  5.2             3.4            1.4             0.2            0\n",
       "146  5.1             3.8            1.6             0.2            0\n",
       "147  5.8             2.6            4.0             1.2            1\n",
       "148  5.7             3.8            1.7             0.3            0\n",
       "149  6.2             2.9            4.3             1.3            1"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = vaex.ml.datasets.load_iris()\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T13:51:40.672938Z",
     "start_time": "2020-06-04T13:51:40.594200Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "df.scatter(df.petal_length, df.petal_width, c_expr=df.class_);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preprocessing \n",
    "\n",
    "### Scaling of numerical features\n",
    "\n",
    "`vaex.ml` packs the common numerical scalers:\n",
    "\n",
    "* `vaex.ml.StandardScaler` - Scale features by removing their mean and dividing by their variance;\n",
    "* `vaex.ml.MinMaxScaler` - Scale features to a given range;\n",
    "* `vaex.ml.RobustScaler` - Scale features by removing their median and scaling them according to a given percentile range;\n",
    "* `vaex.ml.MaxAbsScaler` - Scale features by their maximum absolute value.\n",
    " \n",
    "The usage is quite similar to that of `scikit-learn`, in the sense that each transformer implements the `.fit` and `.transform` methods."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T13:51:40.925129Z",
     "start_time": "2020-06-04T13:51:40.849824Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>scaled_petal_length  </th><th>scaled_petal_width   </th><th>scaled_sepal_length  </th><th>scaled_sepal_width  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>0.25096730693923325  </td><td>0.39617188299171285  </td><td>0.06866179325140277  </td><td>-0.12495760117130607</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>0.4784301228962429   </td><td>0.26469891297233916  </td><td>0.3109975341387059   </td><td>-0.12495760117130607</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>0.4784301228962429   </td><td>0.13322594295296575  </td><td>0.9168368863569659   </td><td>-0.3563605663033572 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>1.1039528667780207   </td><td>1.1850097031079545   </td><td>1.0380047568006185   </td><td>0.5692512942248463  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>-1.341272404759837   </td><td>-1.3129767272601438  </td><td>-0.4160096885232057  </td><td>2.6518779804133055  </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                  </td><td>...                  </td><td>...                  </td><td>...                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>-1.341272404759837   </td><td>-1.3129767272601438  </td><td>-0.7795132998541615  </td><td>0.8006542593568975  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>-1.2275409967813318  </td><td>-1.3129767272601438  </td><td>-0.9006811702978141  </td><td>1.726266119885101   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>0.13723589896072813  </td><td>0.0017529729335920385</td><td>-0.052506077192249874</td><td>-1.0505694616995096 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>-1.1706752927920796  </td><td>-1.18150375724077    </td><td>-0.17367394763590144 </td><td>1.726266119885101   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>0.30783301092848553  </td><td>0.13322594295296575  </td><td>0.4321654045823586   </td><td>-0.3563605663033572 </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    scaled_petal_length    scaled_petal_width     scaled_sepal_length    scaled_sepal_width\n",
       "0    5.9             3.0            4.2             1.5            1         0.25096730693923325    0.39617188299171285    0.06866179325140277    -0.12495760117130607\n",
       "1    6.1             3.0            4.6             1.4            1         0.4784301228962429     0.26469891297233916    0.3109975341387059     -0.12495760117130607\n",
       "2    6.6             2.9            4.6             1.3            1         0.4784301228962429     0.13322594295296575    0.9168368863569659     -0.3563605663033572\n",
       "3    6.7             3.3            5.7             2.1            2         1.1039528667780207     1.1850097031079545     1.0380047568006185     0.5692512942248463\n",
       "4    5.5             4.2            1.4             0.2            0         -1.341272404759837     -1.3129767272601438    -0.4160096885232057    2.6518779804133055\n",
       "...  ...             ...            ...             ...            ...       ...                    ...                    ...                    ...\n",
       "145  5.2             3.4            1.4             0.2            0         -1.341272404759837     -1.3129767272601438    -0.7795132998541615    0.8006542593568975\n",
       "146  5.1             3.8            1.6             0.2            0         -1.2275409967813318    -1.3129767272601438    -0.9006811702978141    1.726266119885101\n",
       "147  5.8             2.6            4.0             1.2            1         0.13723589896072813    0.0017529729335920385  -0.052506077192249874  -1.0505694616995096\n",
       "148  5.7             3.8            1.7             0.3            0         -1.1706752927920796    -1.18150375724077      -0.17367394763590144   1.726266119885101\n",
       "149  6.2             2.9            4.3             1.3            1         0.30783301092848553    0.13322594295296575    0.4321654045823586     -0.3563605663033572"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "scaler = vaex.ml.StandardScaler(features=features, prefix='scaled_')\n",
    "scaler.fit(df)\n",
    "df_trans = scaler.transform(df)\n",
    "df_trans"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The output of the `.transform` method of any `vaex.ml` transformer is a _shallow copy_ of a DataFrame that contains the resulting features of the transformations in addition to the original columns. A shallow copy means that this new DataFrame just references the original one, and no extra memory is used. In addition, the resulting features, in this case the scaled numerical features are _virtual columns,_ which do not take any memory but are computed on the fly when needed. This approach is ideal for working with very large datasets."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Encoding of categorical features\n",
    "\n",
    "`vaex.ml` contains several categorical encoders:\n",
    "\n",
    "* `vaex.ml.LabelEncoder` - Encoding features with as many integers as categories, startinfg from 0;\n",
    "* `vaex.ml.OneHotEncoder` - Encoding features according to the one-hot scheme;\n",
    "* `vaex.ml.FrequencyEncoder` - Encode features by the frequency of their respective categories;\n",
    "* `vaex.ml.BayesianTargetEncoder` - Encode categories with the mean of their target value;\n",
    "* `vaex.ml.WeightOfEvidenceEncoder` - Encode categories their weight of evidence value.\n",
    " \n",
    " The following is a quick example using the Titanic dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T13:51:41.949125Z",
     "start_time": "2020-06-04T13:51:41.929232Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                            </th><th style=\"text-align: right;\">  pclass</th><th>survived  </th><th>name                                           </th><th>sex   </th><th style=\"text-align: right;\">    age</th><th style=\"text-align: right;\">  sibsp</th><th style=\"text-align: right;\">  parch</th><th style=\"text-align: right;\">  ticket</th><th style=\"text-align: right;\">   fare</th><th>cabin  </th><th>embarked  </th><th>boat  </th><th style=\"text-align: right;\">  body</th><th>home_dest                      </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allen, Miss. Elisabeth Walton                  </td><td>female</td><td style=\"text-align: right;\">29     </td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">   24160</td><td style=\"text-align: right;\">211.338</td><td>B5     </td><td>S         </td><td>2     </td><td style=\"text-align: right;\">   nan</td><td>St Louis, MO                   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allison, Master. Hudson Trevor                 </td><td>male  </td><td style=\"text-align: right;\"> 0.9167</td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>11    </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Miss. Helen Loraine                   </td><td>female</td><td style=\"text-align: right;\"> 2     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mr. Hudson Joshua Creighton           </td><td>male  </td><td style=\"text-align: right;\">30     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   135</td><td>Montreal, PQ / Chesterville, ON</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mrs. Hudson J C (Bessie Waldo Daniels)</td><td>female</td><td style=\"text-align: right;\">25     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "  #    pclass  survived    name                                             sex         age    sibsp    parch    ticket     fare  cabin    embarked    boat      body  home_dest\n",
       "  0         1  True        Allen, Miss. Elisabeth Walton                    female  29             0        0     24160  211.338  B5       S           2          nan  St Louis, MO\n",
       "  1         1  True        Allison, Master. Hudson Trevor                   male     0.9167        1        2    113781  151.55   C22 C26  S           11         nan  Montreal, PQ / Chesterville, ON\n",
       "  2         1  False       Allison, Miss. Helen Loraine                     female   2             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON\n",
       "  3         1  False       Allison, Mr. Hudson Joshua Creighton             male    30             1        2    113781  151.55   C22 C26  S           None       135  Montreal, PQ / Chesterville, ON\n",
       "  4         1  False       Allison, Mrs. Hudson J C (Bessie Waldo Daniels)  female  25             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df =  vaex.ml.datasets.load_titanic()\n",
    "df.head(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T13:51:42.542920Z",
     "start_time": "2020-06-04T13:51:42.385565Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                            </th><th style=\"text-align: right;\">  pclass</th><th>survived  </th><th>name                                           </th><th>sex   </th><th style=\"text-align: right;\">    age</th><th style=\"text-align: right;\">  sibsp</th><th style=\"text-align: right;\">  parch</th><th style=\"text-align: right;\">  ticket</th><th style=\"text-align: right;\">   fare</th><th>cabin  </th><th>embarked  </th><th>boat  </th><th style=\"text-align: right;\">  body</th><th>home_dest                      </th><th style=\"text-align: right;\">  label_encoded_embarked</th><th style=\"text-align: right;\">  embarked_0.0</th><th style=\"text-align: right;\">  embarked_C</th><th style=\"text-align: right;\">  embarked_Q</th><th style=\"text-align: right;\">  embarked_S</th><th style=\"text-align: right;\">  frequency_encoded_embarked</th><th style=\"text-align: right;\">  mean_encoded_embarked</th><th style=\"text-align: right;\">  woe_encoded_embarked</th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allen, Miss. Elisabeth Walton                  </td><td>female</td><td style=\"text-align: right;\">29     </td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">   24160</td><td style=\"text-align: right;\">211.338</td><td>B5     </td><td>S         </td><td>2     </td><td style=\"text-align: right;\">   nan</td><td>St Louis, MO                   </td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allison, Master. Hudson Trevor                 </td><td>male  </td><td style=\"text-align: right;\"> 0.9167</td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>11    </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Miss. Helen Loraine                   </td><td>female</td><td style=\"text-align: right;\"> 2     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mr. Hudson Joshua Creighton           </td><td>male  </td><td style=\"text-align: right;\">30     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   135</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mrs. Hudson J C (Bessie Waldo Daniels)</td><td>female</td><td style=\"text-align: right;\">25     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "  #    pclass  survived    name                                             sex         age    sibsp    parch    ticket     fare  cabin    embarked    boat      body  home_dest                          label_encoded_embarked    embarked_0.0    embarked_C    embarked_Q    embarked_S    frequency_encoded_embarked    mean_encoded_embarked    woe_encoded_embarked\n",
       "  0         1  True        Allen, Miss. Elisabeth Walton                    female  29             0        0     24160  211.338  B5       S           2          nan  St Louis, MO                                            1               0             0             0             1                      0.698243                 0.337472               -0.696431\n",
       "  1         1  True        Allison, Master. Hudson Trevor                   male     0.9167        1        2    113781  151.55   C22 C26  S           11         nan  Montreal, PQ / Chesterville, ON                         1               0             0             0             1                      0.698243                 0.337472               -0.696431\n",
       "  2         1  False       Allison, Miss. Helen Loraine                     female   2             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON                         1               0             0             0             1                      0.698243                 0.337472               -0.696431\n",
       "  3         1  False       Allison, Mr. Hudson Joshua Creighton             male    30             1        2    113781  151.55   C22 C26  S           None       135  Montreal, PQ / Chesterville, ON                         1               0             0             0             1                      0.698243                 0.337472               -0.696431\n",
       "  4         1  False       Allison, Mrs. Hudson J C (Bessie Waldo Daniels)  female  25             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON                         1               0             0             0             1                      0.698243                 0.337472               -0.696431"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "label_encoder = vaex.ml.LabelEncoder(features=['embarked'])\n",
    "one_hot_encoder = vaex.ml.OneHotEncoder(features=['embarked'])\n",
    "freq_encoder = vaex.ml.FrequencyEncoder(features=['embarked'])\n",
    "bayes_encoder = vaex.ml.BayesianTargetEncoder(features=['embarked'], target='survived')\n",
    "woe_encoder = vaex.ml.WeightOfEvidenceEncoder(features=['embarked'], target='survived')\n",
    "\n",
    "df = label_encoder.fit_transform(df)\n",
    "df = one_hot_encoder.fit_transform(df)\n",
    "df = freq_encoder.fit_transform(df)\n",
    "df = bayes_encoder.fit_transform(df)\n",
    "df = woe_encoder.fit_transform(df)\n",
    "\n",
    "df.head(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-02T13:09:43.742926Z",
     "start_time": "2020-01-02T13:09:43.676031Z"
    }
   },
   "source": [
    "Notice that the transformed features are all included in the resulting DataFrame and are appropriately named. This is excellent for the construction of various diagnostic plots, and engineering of more complex features. The fact that the resulting (encoded) features take no memory, allows one to try out or combine a variety of preprocessing steps without spending any extra memory. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Feature Engineering\n",
    "\n",
    "### GroupBy Transformer\n",
    "\n",
    "The `GroupByTransformer` is a handy feature in `vaex-ml` that lets you perform a groupby aggregations on the training data, and then use those aggregations as features in the training and test sets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T13:51:44.931996Z",
     "start_time": "2020-06-04T13:51:44.883682Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                            </th><th style=\"text-align: right;\">  pclass</th><th>survived  </th><th>name                                           </th><th>sex   </th><th style=\"text-align: right;\">    age</th><th style=\"text-align: right;\">  sibsp</th><th style=\"text-align: right;\">  parch</th><th style=\"text-align: right;\">  ticket</th><th style=\"text-align: right;\">   fare</th><th>cabin  </th><th>embarked  </th><th>boat  </th><th style=\"text-align: right;\">  body</th><th>home_dest                      </th><th style=\"text-align: right;\">  label_encoded_embarked</th><th style=\"text-align: right;\">  embarked_0.0</th><th style=\"text-align: right;\">  embarked_C</th><th style=\"text-align: right;\">  embarked_Q</th><th style=\"text-align: right;\">  embarked_S</th><th style=\"text-align: right;\">  frequency_encoded_embarked</th><th style=\"text-align: right;\">  mean_encoded_embarked</th><th style=\"text-align: right;\">  woe_encoded_embarked</th><th style=\"text-align: right;\">  age_mean</th><th style=\"text-align: right;\">  age_std</th><th style=\"text-align: right;\">  fare_mean</th><th style=\"text-align: right;\">  fare_std</th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allen, Miss. Elisabeth Walton                  </td><td>female</td><td style=\"text-align: right;\">29     </td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">      0</td><td style=\"text-align: right;\">   24160</td><td style=\"text-align: right;\">211.338</td><td>B5     </td><td>S         </td><td>2     </td><td style=\"text-align: right;\">   nan</td><td>St Louis, MO                   </td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td><td style=\"text-align: right;\">   39.1599</td><td style=\"text-align: right;\">  14.5224</td><td style=\"text-align: right;\">     87.509</td><td style=\"text-align: right;\">   80.3226</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i></td><td style=\"text-align: right;\">       1</td><td>True      </td><td>Allison, Master. Hudson Trevor                 </td><td>male  </td><td style=\"text-align: right;\"> 0.9167</td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>11    </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td><td style=\"text-align: right;\">   39.1599</td><td style=\"text-align: right;\">  14.5224</td><td style=\"text-align: right;\">     87.509</td><td style=\"text-align: right;\">   80.3226</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Miss. Helen Loraine                   </td><td>female</td><td style=\"text-align: right;\"> 2     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td><td style=\"text-align: right;\">   39.1599</td><td style=\"text-align: right;\">  14.5224</td><td style=\"text-align: right;\">     87.509</td><td style=\"text-align: right;\">   80.3226</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mr. Hudson Joshua Creighton           </td><td>male  </td><td style=\"text-align: right;\">30     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   135</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td><td style=\"text-align: right;\">   39.1599</td><td style=\"text-align: right;\">  14.5224</td><td style=\"text-align: right;\">     87.509</td><td style=\"text-align: right;\">   80.3226</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i></td><td style=\"text-align: right;\">       1</td><td>False     </td><td>Allison, Mrs. Hudson J C (Bessie Waldo Daniels)</td><td>female</td><td style=\"text-align: right;\">25     </td><td style=\"text-align: right;\">      1</td><td style=\"text-align: right;\">      2</td><td style=\"text-align: right;\">  113781</td><td style=\"text-align: right;\">151.55 </td><td>C22 C26</td><td>S         </td><td>None  </td><td style=\"text-align: right;\">   nan</td><td>Montreal, PQ / Chesterville, ON</td><td style=\"text-align: right;\">                       1</td><td style=\"text-align: right;\">             0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           0</td><td style=\"text-align: right;\">           1</td><td style=\"text-align: right;\">                    0.698243</td><td style=\"text-align: right;\">               0.337472</td><td style=\"text-align: right;\">             -0.696431</td><td style=\"text-align: right;\">   39.1599</td><td style=\"text-align: right;\">  14.5224</td><td style=\"text-align: right;\">     87.509</td><td style=\"text-align: right;\">   80.3226</td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "  #    pclass  survived    name                                             sex         age    sibsp    parch    ticket     fare  cabin    embarked    boat      body  home_dest                          label_encoded_embarked    embarked_0.0    embarked_C    embarked_Q    embarked_S    frequency_encoded_embarked    mean_encoded_embarked    woe_encoded_embarked    age_mean    age_std    fare_mean    fare_std\n",
       "  0         1  True        Allen, Miss. Elisabeth Walton                    female  29             0        0     24160  211.338  B5       S           2          nan  St Louis, MO                                            1               0             0             0             1                      0.698243                 0.337472               -0.696431     39.1599    14.5224       87.509     80.3226\n",
       "  1         1  True        Allison, Master. Hudson Trevor                   male     0.9167        1        2    113781  151.55   C22 C26  S           11         nan  Montreal, PQ / Chesterville, ON                         1               0             0             0             1                      0.698243                 0.337472               -0.696431     39.1599    14.5224       87.509     80.3226\n",
       "  2         1  False       Allison, Miss. Helen Loraine                     female   2             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON                         1               0             0             0             1                      0.698243                 0.337472               -0.696431     39.1599    14.5224       87.509     80.3226\n",
       "  3         1  False       Allison, Mr. Hudson Joshua Creighton             male    30             1        2    113781  151.55   C22 C26  S           None       135  Montreal, PQ / Chesterville, ON                         1               0             0             0             1                      0.698243                 0.337472               -0.696431     39.1599    14.5224       87.509     80.3226\n",
       "  4         1  False       Allison, Mrs. Hudson J C (Bessie Waldo Daniels)  female  25             1        2    113781  151.55   C22 C26  S           None       nan  Montreal, PQ / Chesterville, ON                         1               0             0             0             1                      0.698243                 0.337472               -0.696431     39.1599    14.5224       87.509     80.3226"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gbt = vaex.ml.GroupByTransformer(by='pclass', agg={'age': ['mean', 'std'],\n",
    "                                                   'fare': ['mean', 'std'],\n",
    "                                                  })\n",
    "df = gbt.fit_transform(df)\n",
    "df.head(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dimensionality reduction \n",
    "\n",
    "### Principal Component Analysis\n",
    "\n",
    "The [PCA](https://en.wikipedia.org/wiki/Principal_component_analysis) implemented in `vaex.ml` can scale to a very large number of samples, even if that data we want to transform does not fit into RAM. To demonstrate this, let us do a PCA transformation on the Iris dataset. For this example, we have replicated this dataset thousands of times, such that it contains over **1 billion** samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:28:15.252341Z",
     "start_time": "2020-06-04T12:28:15.247722Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of samples in DataFrame: 1,005,000,000\n"
     ]
    }
   ],
   "source": [
    "df = vaex.ml.datasets.load_iris_1e9()\n",
    "n_samples = len(df)\n",
    "print(f'Number of samples in DataFrame: {n_samples:,}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:28:21.965477Z",
     "start_time": "2020-06-04T12:28:15.253052Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[#######################################-] 100.00% elapsed time  :     3.21s =  0.1m =  0.0h \n",
      "[########################################] 100.00% elapsed time  :     3.48s =  0.1m =  0.0h   \n",
      " "
     ]
    }
   ],
   "source": [
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "pca = vaex.ml.PCA(features=features, n_components=4, progress=True)\n",
    "pca.fit(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The PCA transformer implemented in `vaex.ml` can be fit in well under a minute, even when the data comprises 4 columns and 1 billion rows. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:28:21.986720Z",
     "start_time": "2020-06-04T12:28:21.966808Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                                        </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>PCA_0               </th><th>PCA_1               </th><th>PCA_2               </th><th>PCA_3                </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>            </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>-0.5110980605065593 </td><td>0.10228410590356304 </td><td>0.13232789125297972 </td><td>-0.05010053260541139 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>            </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>-0.8901604456482555 </td><td>0.033812442698771844</td><td>-0.00976802890600427</td><td>0.15344820598765638  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>            </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>-1.043297780931131  </td><td>-0.22895691065908289</td><td>-0.41481456509067177</td><td>0.037523545094652395 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>            </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>-2.2758536492460615 </td><td>-0.33338652371861266</td><td>0.28467815436267274 </td><td>0.06223028163413104  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>            </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2.5971594768133293  </td><td>-1.1000219282290817 </td><td>0.1635819152383018  </td><td>0.0989580732080908   </td></tr>\n",
       "<tr><td>...                                      </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                 </td><td>...                 </td><td>...                 </td><td>...                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,995</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2.6398212682946376  </td><td>-0.31929006748789257</td><td>-0.13925337205510252</td><td>-0.06514104909647218 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,996</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>2.537573370908233   </td><td>-0.5103675457771604 </td><td>0.17191840236312456 </td><td>0.1921659495959182   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,997</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>-0.22887904987713997</td><td>0.4022576190687374  </td><td>-0.22736270650644824</td><td>-0.018620454426344163</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,998</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>2.1990779611612568  </td><td>-0.8792440894099185 </td><td>-0.11452146077298497</td><td>-0.025326942114004852</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,999</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>-0.6416902782168356 </td><td>-0.01907117740797057</td><td>-0.2041728767402211 </td><td>0.02050967222275115  </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#              sepal_length    sepal_width    petal_length    petal_width    class_    PCA_0                 PCA_1                 PCA_2                 PCA_3\n",
       "0              5.9             3.0            4.2             1.5            1         -0.5110980605065593   0.10228410590356304   0.13232789125297972   -0.05010053260541139\n",
       "1              6.1             3.0            4.6             1.4            1         -0.8901604456482555   0.033812442698771844  -0.00976802890600427  0.15344820598765638\n",
       "2              6.6             2.9            4.6             1.3            1         -1.043297780931131    -0.22895691065908289  -0.41481456509067177  0.037523545094652395\n",
       "3              6.7             3.3            5.7             2.1            2         -2.2758536492460615   -0.33338652371861266  0.28467815436267274   0.06223028163413104\n",
       "4              5.5             4.2            1.4             0.2            0         2.5971594768133293    -1.1000219282290817   0.1635819152383018    0.0989580732080908\n",
       "...            ...             ...            ...             ...            ...       ...                   ...                   ...                   ...\n",
       "1,004,999,995  5.2             3.4            1.4             0.2            0         2.6398212682946376    -0.31929006748789257  -0.13925337205510252  -0.06514104909647218\n",
       "1,004,999,996  5.1             3.8            1.6             0.2            0         2.537573370908233     -0.5103675457771604   0.17191840236312456   0.1921659495959182\n",
       "1,004,999,997  5.8             2.6            4.0             1.2            1         -0.22887904987713997  0.4022576190687374    -0.22736270650644824  -0.018620454426344163\n",
       "1,004,999,998  5.7             3.8            1.7             0.3            0         2.1990779611612568    -0.8792440894099185   -0.11452146077298497  -0.025326942114004852\n",
       "1,004,999,999  6.2             2.9            4.3             1.3            1         -0.6416902782168356   -0.01907117740797057  -0.2041728767402211   0.02050967222275115"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_trans = pca.transform(df)\n",
    "df_trans"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Recall that the transformed DataFrame, which includes the PCA components, takes no extra memory. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Clustering\n",
    "\n",
    "### K-Means\n",
    "\n",
    "`vaex.ml` implements a fast and scalable K-Means clustering algorithm. The usage is similar to that of `scikit-learn`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:28:22.387577Z",
     "start_time": "2020-06-04T12:28:21.987504Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration    0, inertia  519.0500000000001\n",
      "Iteration    1, inertia  156.70447116074328\n",
      "Iteration    2, inertia  88.70688235734133\n",
      "Iteration    3, inertia  80.23054939305554\n",
      "Iteration    4, inertia  79.28654263977778\n",
      "Iteration    5, inertia  78.94084142614601\n",
      "Iteration    6, inertia  78.94084142614601\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>prediction_kmeans  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>0                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>0                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>0                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>1                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2                  </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>2                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>0                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>2                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>0                  </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    prediction_kmeans\n",
       "0    5.9             3.0            4.2             1.5            1         0\n",
       "1    6.1             3.0            4.6             1.4            1         0\n",
       "2    6.6             2.9            4.6             1.3            1         0\n",
       "3    6.7             3.3            5.7             2.1            2         1\n",
       "4    5.5             4.2            1.4             0.2            0         2\n",
       "...  ...             ...            ...             ...            ...       ...\n",
       "145  5.2             3.4            1.4             0.2            0         2\n",
       "146  5.1             3.8            1.6             0.2            0         2\n",
       "147  5.8             2.6            4.0             1.2            1         0\n",
       "148  5.7             3.8            1.7             0.3            0         2\n",
       "149  6.2             2.9            4.3             1.3            1         0"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import vaex.ml.cluster\n",
    "\n",
    "df = vaex.ml.datasets.load_iris()\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "kmeans = vaex.ml.cluster.KMeans(features=features, n_clusters=3, max_iter=100, verbose=True, random_state=42)\n",
    "kmeans.fit(df)\n",
    "\n",
    "df_trans = kmeans.transform(df)\n",
    "df_trans"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "K-Means is an unsupervised algorithm, meaning that the predicted cluster labels in the transformed dataset do not necessarily correspond to the class label. We can map the predicted cluster identifiers to match the class labels, making it easier to construct diagnostic plots."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:28:22.402420Z",
     "start_time": "2020-06-04T12:28:22.388510Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>prediction_kmeans  </th><th>predicted_kmean_map  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>1                  </td><td>2                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2                  </td><td>0                    </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                </td><td>...                  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>2                  </td><td>0                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>2                  </td><td>0                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>2                  </td><td>0                    </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>0                  </td><td>1                    </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    prediction_kmeans    predicted_kmean_map\n",
       "0    5.9             3.0            4.2             1.5            1         0                    1\n",
       "1    6.1             3.0            4.6             1.4            1         0                    1\n",
       "2    6.6             2.9            4.6             1.3            1         0                    1\n",
       "3    6.7             3.3            5.7             2.1            2         1                    2\n",
       "4    5.5             4.2            1.4             0.2            0         2                    0\n",
       "...  ...             ...            ...             ...            ...       ...                  ...\n",
       "145  5.2             3.4            1.4             0.2            0         2                    0\n",
       "146  5.1             3.8            1.6             0.2            0         2                    0\n",
       "147  5.8             2.6            4.0             1.2            1         0                    1\n",
       "148  5.7             3.8            1.7             0.3            0         2                    0\n",
       "149  6.2             2.9            4.3             1.3            1         0                    1"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_trans['predicted_kmean_map'] = df_trans.prediction_kmeans.map(mapper={0: 1, 1: 2, 2: 0})\n",
    "df_trans"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can construct simple scatter plots, and see that in the case of the Iris dataset, K-Means does a pretty good job splitting the data into 3 classes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:28:22.589451Z",
     "start_time": "2020-06-04T12:28:22.403292Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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E8nxpeBzunBbFqOKuvDkpCnN+wGIyO4oV0lSHdOrD1ePHB1xKzXT4+H108+K2f/Zk06lf27kna1RhUXcc+yEqL6Ww8Hzp5zOwI6dcFsWXqeToowMnj7xZDqmpjU5xtMGHFBbZLlIcbnwWBanDDiAf506lUl9++OxLFJcLlJdbq4bdTAT+/Zr8tgirU2iVxF8DjgcyAXWA/8HuABs2360dunbB6lZVrcCOM+27d0uuaSVmSQRVmzdzLqyckYUFjW65PpO4XCYuWvXkuH1NGkVpMpAgHXlZeSnpuFzufYoPtu2WVtWimUM+Wm7pgCGbZvV27eT7nHHnNYo0hrt41UElaukVQiGw8xdW0yuL5XeTZipsLG8jMWbNzGwYyeyvL4m9C+nOhSkMD1jj7f7qA4GWVtWSseUVFLrTVcsra5mU2UFndPSm7wyoUhL11iuSmiBlShKWiIibdu+LLASRblKRKRtS8oy7SIiIiIiIu2JCiwREREREZE4UYElIiIiIiISJyqwRERERERE4kQFloiIiIiISJyowBIREREREYkTFVgiIiIiIiJxogJLREREREQkTlRgiYiIiIiIxIkKLBERERERkThRgSUiIiIiIhInKrBERERERETiRAWWiIiIiIhInKjAEhERERERiRMVWCIiIiIiInGiAktERERERCROVGCJiIiIiIjEiQosERERERGROFGBJSIiIiIiEicqsEREREREROJEBZaIiIiIiEicqMASERERERGJExVYIiIiIiIicaICS0REREREJE5UYImIiIiIiMSJCiwREREREZE4UYElIiIiIiISJyqwRERERERE4kQFloiIiIiISJyowBIREREREYkTFVgiIiIiIiJxogJLREREREQkTlRgiYiIiIiIxIkKLBERERERkThRgSUiIiIiIhInzmQH0F7Zts2Hz3/GS/e8wY7NZYyaOJSpN02hQ352skMTEREBYPumHTx3+8vMeG0uqZkpnHT1RMadPRZjTLJDExFpsVRgJclTv3qWVx94m6ryagDe/Mt/+PjFmTyx8G4yctKTHJ2IiLR35dvLuXToDWxdv52gPwjA/Zc/zpIvlnP5fecnOToRkZZLUwSTYPumHbx877/riiuAYCBE+bZyXnvonSRGJiIiUuPfj3/Ajk2ldcUVQFV5Nf9+7H02rd2SxMhERFo2FVhJsPyrlbg8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\n",
      "text/plain": [
       "<Figure size 864x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure(figsize=(12, 5))\n",
    "\n",
    "plt.subplot(121)\n",
    "df_trans.scatter(df_trans.petal_length, df_trans.petal_width, c_expr=df_trans.class_)\n",
    "plt.title('Original classes')\n",
    "\n",
    "plt.subplot(122)\n",
    "df_trans.scatter(df_trans.petal_length, df_trans.petal_width, c_expr=df_trans.predicted_kmean_map)\n",
    "plt.title('Predicted classes')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As with any algorithm implemented in `vaex.ml`, K-Means can be used on billions of samples. Fitting takes **under 2 minutes** when applied on the oversampled Iris dataset, numbering over **1 billion** samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:28:22.594840Z",
     "start_time": "2020-06-04T12:28:22.590440Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of samples in DataFrame: 1,005,000,000\n"
     ]
    }
   ],
   "source": [
    "df = vaex.ml.datasets.load_iris_1e9()\n",
    "n_samples = len(df)\n",
    "print(f'Number of samples in DataFrame: {n_samples:,}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:28:42.151162Z",
     "start_time": "2020-06-04T12:28:22.595703Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration    0, inertia  838974000.0037191\n",
      "Iteration    1, inertia  535903134.000306\n",
      "Iteration    2, inertia  530190921.48488975\n",
      "Iteration    3, inertia  528931941.0337243\n",
      "Iteration    4, inertia  528931941.0337246\n",
      "CPU times: user 2min 33s, sys: 1.02 s, total: 2min 34s\n",
      "Wall time: 19.5 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "kmeans = vaex.ml.cluster.KMeans(features=features, n_clusters=3, max_iter=100, verbose=True, random_state=31)\n",
    "kmeans.fit(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Supervised learning\n",
    "\n",
    "While `vaex.ml` does not yet implement any supervised machine learning models, it does provide wrappers to several popular libraries such as [scikit-learn](https://scikit-learn.org/), [XGBoost](https://xgboost.readthedocs.io/), [LightGBM](https://lightgbm.readthedocs.io/) and [CatBoost](https://catboost.ai/). \n",
    "\n",
    "The main benefit of these wrappers is that they turn the models into `vaex.ml` transformers. This means the models become part of the DataFrame _state_ and thus can be serialized, and their predictions can be returned as _virtual columns_. This is especially useful for creating various diagnostic plots and evaluating performance metrics at no memory cost, as well as building ensembles. \n",
    "\n",
    "### `Scikit-Learn` example\n",
    "\n",
    "The `vaex.ml.sklearn` module provides convenient wrappers to the `scikit-learn` estimators. In fact, these wrappers can be used with any library that follows the API convention established by `scikit-learn`, i.e. implements the `.fit` and `.transform` methods.\n",
    "\n",
    "Here is an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:28:42.580374Z",
     "start_time": "2020-06-04T12:28:42.152552Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>145</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>146</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>147</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>148</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>149</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>1           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    prediction\n",
       "0    5.9             3.0            4.2             1.5            1         1\n",
       "1    6.1             3.0            4.6             1.4            1         1\n",
       "2    6.6             2.9            4.6             1.3            1         1\n",
       "3    6.7             3.3            5.7             2.1            2         2\n",
       "4    5.5             4.2            1.4             0.2            0         0\n",
       "...  ...             ...            ...             ...            ...       ...\n",
       "145  5.2             3.4            1.4             0.2            0         0\n",
       "146  5.1             3.8            1.6             0.2            0         0\n",
       "147  5.8             2.6            4.0             1.2            1         1\n",
       "148  5.7             3.8            1.7             0.3            0         0\n",
       "149  6.2             2.9            4.3             1.3            1         1"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from vaex.ml.sklearn import Predictor\n",
    "from sklearn.ensemble import GradientBoostingClassifier\n",
    "\n",
    "df = vaex.ml.datasets.load_iris()\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "target = 'class_'\n",
    "\n",
    "model = GradientBoostingClassifier(random_state=42)\n",
    "vaex_model = Predictor(features=features, target=target, model=model, prediction_name='prediction')\n",
    "\n",
    "vaex_model.fit(df=df)\n",
    "\n",
    "df = vaex_model.transform(df)\n",
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One can still train a predictive model on datasets that are too big to fit into memory by leveraging the on-line learners provided by `scikit-learn`. The `vaex.ml.sklearn.IncrementalPredictor` conveniently wraps these learners and provides control on how the data is passed to them from a `vaex` DataFrame. \n",
    "\n",
    "Let us train a model on the oversampled Iris dataset which comprises over 1 billion samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:37:47.105199Z",
     "start_time": "2020-06-04T12:28:42.581284Z"
    },
    "tags": [
     "skip-ci"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[########################################] 100.00% elapsed time  :   544.42s =  9.1m =  0.2h\n",
      " "
     ]
    },
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                                        </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>            </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>            </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>            </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>            </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>            </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td>...                                      </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,995</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,996</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,997</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,998</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1,004,999,999</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>1           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#              sepal_length    sepal_width    petal_length    petal_width    class_    prediction\n",
       "0              5.9             3.0            4.2             1.5            1         1\n",
       "1              6.1             3.0            4.6             1.4            1         1\n",
       "2              6.6             2.9            4.6             1.3            1         1\n",
       "3              6.7             3.3            5.7             2.1            2         2\n",
       "4              5.5             4.2            1.4             0.2            0         0\n",
       "...            ...             ...            ...             ...            ...       ...\n",
       "1,004,999,995  5.2             3.4            1.4             0.2            0         0\n",
       "1,004,999,996  5.1             3.8            1.6             0.2            0         0\n",
       "1,004,999,997  5.8             2.6            4.0             1.2            1         1\n",
       "1,004,999,998  5.7             3.8            1.7             0.3            0         0\n",
       "1,004,999,999  6.2             2.9            4.3             1.3            1         1"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from vaex.ml.sklearn import IncrementalPredictor\n",
    "from sklearn.linear_model import SGDClassifier\n",
    "\n",
    "df = vaex.ml.datasets.load_iris_1e9()\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "target = 'class_'\n",
    "\n",
    "model = SGDClassifier(learning_rate='constant', eta0=0.0001, random_state=42)\n",
    "vaex_model = IncrementalPredictor(features=features, target=target, model=model, \n",
    "                                  batch_size=11_000_000, partial_fit_kwargs={'classes':[0, 1, 2]})\n",
    "\n",
    "vaex_model.fit(df=df, progress=True)\n",
    "\n",
    "df = vaex_model.transform(df)\n",
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `XGBoost` example\n",
    "\n",
    "Libraries such as `XGBoost` provide more options such as validation during training and early stopping for example. We provide wrappers that keeps close to the native API of these libraries, in addition to the `scikit-learn` API. \n",
    "\n",
    "While the following example showcases the `XGBoost` wrapper, `vaex.ml` implements similar wrappers for `LightGBM` and `CatBoost`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:37:47.975165Z",
     "start_time": "2020-06-04T12:37:47.106193Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                                 </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>xgboost_prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>     </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>     </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>     </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>     </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>     </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0.0                 </td></tr>\n",
       "<tr><td>...                               </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,395</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>0.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,396</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>0.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,397</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,398</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>0.0                 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>80,399</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>1.0                 </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#       sepal_length    sepal_width    petal_length    petal_width    class_    xgboost_prediction\n",
       "0       5.9             3.0            4.2             1.5            1         1.0\n",
       "1       6.1             3.0            4.6             1.4            1         1.0\n",
       "2       6.6             2.9            4.6             1.3            1         1.0\n",
       "3       6.7             3.3            5.7             2.1            2         2.0\n",
       "4       5.5             4.2            1.4             0.2            0         0.0\n",
       "...     ...             ...            ...             ...            ...       ...\n",
       "80,395  5.2             3.4            1.4             0.2            0         0.0\n",
       "80,396  5.1             3.8            1.6             0.2            0         0.0\n",
       "80,397  5.8             2.6            4.0             1.2            1         1.0\n",
       "80,398  5.7             3.8            1.7             0.3            0         0.0\n",
       "80,399  6.2             2.9            4.3             1.3            1         1.0"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from vaex.ml.xgboost import XGBoostModel\n",
    "\n",
    "df = vaex.ml.datasets.load_iris_1e5()\n",
    "df_train, df_test = df.ml.train_test_split(test_size=0.2, verbose=False)\n",
    "\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width']\n",
    "target = 'class_'\n",
    "\n",
    "params = {'learning_rate': 0.1,\n",
    "          'max_depth': 3, \n",
    "          'num_class': 3, \n",
    "          'objective': 'multi:softmax',\n",
    "          'subsample': 1,\n",
    "          'random_state': 42,\n",
    "          'n_jobs': -1}\n",
    "\n",
    "\n",
    "booster = XGBoostModel(features=features, target=target, num_boost_round=500, params=params)\n",
    "booster.fit(df=df_train, evals=[(df_train, 'train'), (df_test, 'test')], early_stopping_rounds=5)\n",
    "\n",
    "df_test = booster.transform(df_train)\n",
    "df_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## State transfer - pipelines made easy\n",
    "\n",
    "Each `vaex` DataFrame consists of two parts: _data_ and _state_. The _data_ is immutable, and any operation such as filtering, adding new columns, or applying transformers or predictive models just modifies the _state_. This is extremely powerful concept and can completely redefine how we imagine machine learning pipelines. \n",
    "\n",
    "As an example, let us once again create a model based on the Iris dataset. Here, we will create a couple of new features, do a PCA transformation, and finally train a predictive model. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:37:48.027452Z",
     "start_time": "2020-06-04T12:37:47.976368Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>petal_ratio       </th><th>sepal_ratio       </th><th>PCA_0              </th><th>PCA_1              </th><th>PCA_2                 </th><th>PCA_3               </th><th>PCA_4                 </th><th>PCA_5                 </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.4           </td><td>3.0          </td><td>4.5           </td><td>1.5          </td><td>1       </td><td>3.0               </td><td>1.8               </td><td>-1.510547480171215 </td><td>0.3611524321126822 </td><td>-0.4005106138591812   </td><td>0.5491844107628985  </td><td>0.21135370342329635   </td><td>-0.009542243224854377 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>4.8           </td><td>3.4          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>8.0               </td><td>1.411764705882353 </td><td>4.447550641536847  </td><td>0.2799644730487585 </td><td>-0.04904458661276928  </td><td>0.18719360579644695 </td><td>0.10928493945448532   </td><td>0.005228919010020094  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.9           </td><td>3.1          </td><td>4.9           </td><td>1.5          </td><td>1       </td><td>3.266666666666667 </td><td>2.2258064516129035</td><td>-1.777649528149752 </td><td>-0.6082889770845891</td><td>0.48007833550651513   </td><td>-0.37762011866831335</td><td>0.05174472701894024   </td><td>-0.04673816474220924  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>4.4           </td><td>3.2          </td><td>1.3           </td><td>0.2          </td><td>0       </td><td>6.5               </td><td>1.375             </td><td>3.400548263702555  </td><td>1.437036928591846  </td><td>-0.3662652846960042   </td><td>0.23420836198441913 </td><td>0.05750021481634099   </td><td>-0.023055011653267066 </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.6           </td><td>2.8          </td><td>4.9           </td><td>2.0          </td><td>2       </td><td>2.45              </td><td>2.0               </td><td>-2.3245098766222094</td><td>0.14710673877401348</td><td>-0.5150809942258257   </td><td>0.5471824391426298  </td><td>-0.12154714382375817  </td><td>0.0044686197532133876 </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...               </td><td>...               </td><td>...                </td><td>...                </td><td>...                   </td><td>...                 </td><td>...                   </td><td>...                   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>115</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.5294117647058825</td><td>3.623794583238953  </td><td>0.8255759252729563 </td><td>0.23453320686724874   </td><td>-0.17599408825208826</td><td>-0.04687036865354327  </td><td>-0.02424621891240747  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>116</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>8.0               </td><td>1.3421052631578947</td><td>4.42115266246093   </td><td>0.22287505533663704</td><td>0.4450642830179705    </td><td>0.2184424557783562  </td><td>0.14504752606375293   </td><td>0.07229123907677276   </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>117</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>3.3333333333333335</td><td>2.230769230769231 </td><td>-1.069062832993727 </td><td>0.3874258314654399 </td><td>-0.4471767749236783   </td><td>-0.2956609879568117 </td><td>-0.0010695982441835394</td><td>-0.0065225306610744715</td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>118</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>5.666666666666667 </td><td>1.5000000000000002</td><td>2.2846521048417037 </td><td>1.1920826609681359 </td><td>0.8273738848637026    </td><td>-0.21048946462725737</td><td>0.03381892388998425   </td><td>0.018792165273013528  </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>119</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>3.3076923076923075</td><td>2.137931034482759 </td><td>-1.2988229958748452</td><td>0.06960434514054464</td><td>-0.0012167985718341268</td><td>-0.24072255219180883</td><td>0.05282732890885841   </td><td>-0.032459999314411514 </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    petal_ratio         sepal_ratio         PCA_0                PCA_1                PCA_2                   PCA_3                 PCA_4                   PCA_5\n",
       "0    5.4             3.0            4.5             1.5            1         3.0                 1.8                 -1.510547480171215   0.3611524321126822   -0.4005106138591812     0.5491844107628985    0.21135370342329635     -0.009542243224854377\n",
       "1    4.8             3.4            1.6             0.2            0         8.0                 1.411764705882353   4.447550641536847    0.2799644730487585   -0.04904458661276928    0.18719360579644695   0.10928493945448532     0.005228919010020094\n",
       "2    6.9             3.1            4.9             1.5            1         3.266666666666667   2.2258064516129035  -1.777649528149752   -0.6082889770845891  0.48007833550651513     -0.37762011866831335  0.05174472701894024     -0.04673816474220924\n",
       "3    4.4             3.2            1.3             0.2            0         6.5                 1.375               3.400548263702555    1.437036928591846    -0.3662652846960042     0.23420836198441913   0.05750021481634099     -0.023055011653267066\n",
       "4    5.6             2.8            4.9             2.0            2         2.45                2.0                 -2.3245098766222094  0.14710673877401348  -0.5150809942258257     0.5471824391426298    -0.12154714382375817    0.0044686197532133876\n",
       "...  ...             ...            ...             ...            ...       ...                 ...                 ...                  ...                  ...                     ...                   ...                     ...\n",
       "115  5.2             3.4            1.4             0.2            0         6.999999999999999   1.5294117647058825  3.623794583238953    0.8255759252729563   0.23453320686724874     -0.17599408825208826  -0.04687036865354327    -0.02424621891240747\n",
       "116  5.1             3.8            1.6             0.2            0         8.0                 1.3421052631578947  4.42115266246093     0.22287505533663704  0.4450642830179705      0.2184424557783562    0.14504752606375293     0.07229123907677276\n",
       "117  5.8             2.6            4.0             1.2            1         3.3333333333333335  2.230769230769231   -1.069062832993727   0.3874258314654399   -0.4471767749236783     -0.2956609879568117   -0.0010695982441835394  -0.0065225306610744715\n",
       "118  5.7             3.8            1.7             0.3            0         5.666666666666667   1.5000000000000002  2.2846521048417037   1.1920826609681359   0.8273738848637026      -0.21048946462725737  0.03381892388998425     0.018792165273013528\n",
       "119  6.2             2.9            4.3             1.3            1         3.3076923076923075  2.137931034482759   -1.2988229958748452  0.06960434514054464  -0.0012167985718341268  -0.24072255219180883  0.05282732890885841     -0.032459999314411514"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Load data and split it in train and test sets\n",
    "df = vaex.ml.datasets.load_iris()\n",
    "df_train, df_test = df.ml.train_test_split(test_size=0.2, verbose=False)\n",
    "\n",
    "# Create new features\n",
    "df_train['petal_ratio'] = df_train.petal_length / df_train.petal_width\n",
    "df_train['sepal_ratio'] = df_train.sepal_length / df_train.sepal_width\n",
    "\n",
    "# Do a PCA transformation\n",
    "features = ['petal_length', 'petal_width', 'sepal_length', 'sepal_width', 'petal_ratio', 'sepal_ratio']\n",
    "pca = vaex.ml.PCA(features=features, n_components=6)\n",
    "df_train = pca.fit_transform(df_train)\n",
    "\n",
    "# Display the training DataFrame at this stage\n",
    "df_train"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this point, we are ready to train a predictive model. In this example, let's use `LightGBM` with its `scikit-learn` API. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:37:48.136319Z",
     "start_time": "2020-06-04T12:37:48.028245Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                              </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>petal_ratio       </th><th>sepal_ratio       </th><th>PCA_0              </th><th>PCA_1              </th><th>PCA_2                 </th><th>PCA_3               </th><th>PCA_4                 </th><th>PCA_5                 </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i>  </td><td>5.4           </td><td>3.0          </td><td>4.5           </td><td>1.5          </td><td>1       </td><td>3.0               </td><td>1.8               </td><td>-1.510547480171215 </td><td>0.3611524321126822 </td><td>-0.4005106138591812   </td><td>0.5491844107628985  </td><td>0.21135370342329635   </td><td>-0.009542243224854377 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i>  </td><td>4.8           </td><td>3.4          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>8.0               </td><td>1.411764705882353 </td><td>4.447550641536847  </td><td>0.2799644730487585 </td><td>-0.04904458661276928  </td><td>0.18719360579644695 </td><td>0.10928493945448532   </td><td>0.005228919010020094  </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i>  </td><td>6.9           </td><td>3.1          </td><td>4.9           </td><td>1.5          </td><td>1       </td><td>3.266666666666667 </td><td>2.2258064516129035</td><td>-1.777649528149752 </td><td>-0.6082889770845891</td><td>0.48007833550651513   </td><td>-0.37762011866831335</td><td>0.05174472701894024   </td><td>-0.04673816474220924  </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i>  </td><td>4.4           </td><td>3.2          </td><td>1.3           </td><td>0.2          </td><td>0       </td><td>6.5               </td><td>1.375             </td><td>3.400548263702555  </td><td>1.437036928591846  </td><td>-0.3662652846960042   </td><td>0.23420836198441913 </td><td>0.05750021481634099   </td><td>-0.023055011653267066 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i>  </td><td>5.6           </td><td>2.8          </td><td>4.9           </td><td>2.0          </td><td>2       </td><td>2.45              </td><td>2.0               </td><td>-2.3245098766222094</td><td>0.14710673877401348</td><td>-0.5150809942258257   </td><td>0.5471824391426298  </td><td>-0.12154714382375817  </td><td>0.0044686197532133876 </td><td>2           </td></tr>\n",
       "<tr><td>...                            </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...               </td><td>...               </td><td>...                </td><td>...                </td><td>...                   </td><td>...                 </td><td>...                   </td><td>...                   </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>115</i></td><td>5.2           </td><td>3.4          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.5294117647058825</td><td>3.623794583238953  </td><td>0.8255759252729563 </td><td>0.23453320686724874   </td><td>-0.17599408825208826</td><td>-0.04687036865354327  </td><td>-0.02424621891240747  </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>116</i></td><td>5.1           </td><td>3.8          </td><td>1.6           </td><td>0.2          </td><td>0       </td><td>8.0               </td><td>1.3421052631578947</td><td>4.42115266246093   </td><td>0.22287505533663704</td><td>0.4450642830179705    </td><td>0.2184424557783562  </td><td>0.14504752606375293   </td><td>0.07229123907677276   </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>117</i></td><td>5.8           </td><td>2.6          </td><td>4.0           </td><td>1.2          </td><td>1       </td><td>3.3333333333333335</td><td>2.230769230769231 </td><td>-1.069062832993727 </td><td>0.3874258314654399 </td><td>-0.4471767749236783   </td><td>-0.2956609879568117 </td><td>-0.0010695982441835394</td><td>-0.0065225306610744715</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>118</i></td><td>5.7           </td><td>3.8          </td><td>1.7           </td><td>0.3          </td><td>0       </td><td>5.666666666666667 </td><td>1.5000000000000002</td><td>2.2846521048417037 </td><td>1.1920826609681359 </td><td>0.8273738848637026    </td><td>-0.21048946462725737</td><td>0.03381892388998425   </td><td>0.018792165273013528  </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>119</i></td><td>6.2           </td><td>2.9          </td><td>4.3           </td><td>1.3          </td><td>1       </td><td>3.3076923076923075</td><td>2.137931034482759 </td><td>-1.2988229958748452</td><td>0.06960434514054464</td><td>-0.0012167985718341268</td><td>-0.24072255219180883</td><td>0.05282732890885841   </td><td>-0.032459999314411514 </td><td>1           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    petal_ratio         sepal_ratio         PCA_0                PCA_1                PCA_2                   PCA_3                 PCA_4                   PCA_5                   prediction\n",
       "0    5.4             3.0            4.5             1.5            1         3.0                 1.8                 -1.510547480171215   0.3611524321126822   -0.4005106138591812     0.5491844107628985    0.21135370342329635     -0.009542243224854377   1\n",
       "1    4.8             3.4            1.6             0.2            0         8.0                 1.411764705882353   4.447550641536847    0.2799644730487585   -0.04904458661276928    0.18719360579644695   0.10928493945448532     0.005228919010020094    0\n",
       "2    6.9             3.1            4.9             1.5            1         3.266666666666667   2.2258064516129035  -1.777649528149752   -0.6082889770845891  0.48007833550651513     -0.37762011866831335  0.05174472701894024     -0.04673816474220924    1\n",
       "3    4.4             3.2            1.3             0.2            0         6.5                 1.375               3.400548263702555    1.437036928591846    -0.3662652846960042     0.23420836198441913   0.05750021481634099     -0.023055011653267066   0\n",
       "4    5.6             2.8            4.9             2.0            2         2.45                2.0                 -2.3245098766222094  0.14710673877401348  -0.5150809942258257     0.5471824391426298    -0.12154714382375817    0.0044686197532133876   2\n",
       "...  ...             ...            ...             ...            ...       ...                 ...                 ...                  ...                  ...                     ...                   ...                     ...                     ...\n",
       "115  5.2             3.4            1.4             0.2            0         6.999999999999999   1.5294117647058825  3.623794583238953    0.8255759252729563   0.23453320686724874     -0.17599408825208826  -0.04687036865354327    -0.02424621891240747    0\n",
       "116  5.1             3.8            1.6             0.2            0         8.0                 1.3421052631578947  4.42115266246093     0.22287505533663704  0.4450642830179705      0.2184424557783562    0.14504752606375293     0.07229123907677276     0\n",
       "117  5.8             2.6            4.0             1.2            1         3.3333333333333335  2.230769230769231   -1.069062832993727   0.3874258314654399   -0.4471767749236783     -0.2956609879568117   -0.0010695982441835394  -0.0065225306610744715  1\n",
       "118  5.7             3.8            1.7             0.3            0         5.666666666666667   1.5000000000000002  2.2846521048417037   1.1920826609681359   0.8273738848637026      -0.21048946462725737  0.03381892388998425     0.018792165273013528    0\n",
       "119  6.2             2.9            4.3             1.3            1         3.3076923076923075  2.137931034482759   -1.2988229958748452  0.06960434514054464  -0.0012167985718341268  -0.24072255219180883  0.05282732890885841     -0.032459999314411514   1"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import lightgbm\n",
    "\n",
    "features = df_train.get_column_names(regex='^PCA')\n",
    "\n",
    "booster = lightgbm.LGBMClassifier()\n",
    "\n",
    "vaex_model = Predictor(model=booster, features=features, target='class_')\n",
    "\n",
    "vaex_model.fit(df=df_train)\n",
    "df_train = vaex_model.transform(df_train)\n",
    "\n",
    "df_train"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The final `df_train` DataFrame contains all the features we created, including the predictions right at the end. Now, we would like to apply the same transformations to the test set. All we need to do, is to simply extract the _state_ from `df_train` and apply it to `df_test`. This will propagate all the changes that were made to the training set on the test set.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:37:48.197139Z",
     "start_time": "2020-06-04T12:37:48.137509Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                             </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>petal_ratio       </th><th>sepal_ratio       </th><th>PCA_0              </th><th>PCA_1               </th><th>PCA_2               </th><th>PCA_3               </th><th>PCA_4               </th><th>PCA_5                </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i> </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>2.8000000000000003</td><td>1.9666666666666668</td><td>-1.642627940409072 </td><td>0.49931302910747727 </td><td>-0.06308800806664466</td><td>0.10842057110641677 </td><td>-0.03924298664189224</td><td>-0.027394439700272822</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i> </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>3.2857142857142856</td><td>2.033333333333333 </td><td>-1.445047446393471 </td><td>-0.1019091578746504 </td><td>-0.01899012239493801</td><td>0.020980767646090408</td><td>0.1614215276667148  </td><td>-0.02716639637934938 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i> </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>3.538461538461538 </td><td>2.2758620689655173</td><td>-1.330564613235537 </td><td>-0.41978474749131267</td><td>0.1759590589290671  </td><td>-0.4631301992308477 </td><td>0.08304243689815374 </td><td>-0.033351733677429274</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i> </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2.7142857142857144</td><td>2.0303030303030303</td><td>-2.6719170661531013</td><td>-0.9149428897499291 </td><td>0.4156162725009377  </td><td>0.34633692661436644 </td><td>0.03742964707590906 </td><td>-0.013254286196245774</td><td>2           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i> </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.3095238095238095</td><td>3.6322930267831404 </td><td>0.8198526437905096  </td><td>1.046277579362938   </td><td>0.09738737839850209 </td><td>0.09412658096734221 </td><td>0.1329137026697501   </td><td>0           </td></tr>\n",
       "<tr><td>...                           </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...               </td><td>...               </td><td>...                </td><td>...                 </td><td>...                 </td><td>...                 </td><td>...                 </td><td>...                  </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>25</i></td><td>5.5           </td><td>2.5          </td><td>4.0           </td><td>1.3          </td><td>1       </td><td>3.0769230769230766</td><td>2.2               </td><td>-1.2523120088600896</td><td>0.5975071562677784  </td><td>-0.7019801415469216 </td><td>-0.11489031841855571</td><td>-0.03615945782087869</td><td>0.005496321827264977 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>26</i></td><td>5.8           </td><td>2.7          </td><td>3.9           </td><td>1.2          </td><td>1       </td><td>3.25              </td><td>2.148148148148148 </td><td>-1.0792352165904657</td><td>0.5236883751378523  </td><td>-0.34037717939532286</td><td>-0.23743695029955128</td><td>-0.00936891422024664</td><td>-0.02184110533380834 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>27</i></td><td>4.4           </td><td>2.9          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.517241379310345 </td><td>3.7422969192506095 </td><td>1.048460304741977   </td><td>-0.636475521315278  </td><td>0.07623157913054074 </td><td>0.004215355833312173</td><td>-0.06354157393133958 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>28</i></td><td>4.5           </td><td>2.3          </td><td>1.3           </td><td>0.3          </td><td>0       </td><td>4.333333333333334 </td><td>1.956521739130435 </td><td>1.4537380535696471 </td><td>2.4197864889383505  </td><td>-1.0301500321688102 </td><td>-0.5150263062576134 </td><td>-0.2631218962099228 </td><td>-0.06608059456656257 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>29</i></td><td>6.9           </td><td>3.2          </td><td>5.7           </td><td>2.3          </td><td>2       </td><td>2.4782608695652177</td><td>2.15625           </td><td>-2.963110301521378 </td><td>-0.924626055589704  </td><td>0.44833006106219797 </td><td>0.20994670504662372 </td><td>-0.2012725506779131 </td><td>-0.018900414287719353</td><td>2           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    petal_ratio         sepal_ratio         PCA_0                PCA_1                 PCA_2                 PCA_3                 PCA_4                 PCA_5                  prediction\n",
       "0    5.9             3.0            4.2             1.5            1         2.8000000000000003  1.9666666666666668  -1.642627940409072   0.49931302910747727   -0.06308800806664466  0.10842057110641677   -0.03924298664189224  -0.027394439700272822  1\n",
       "1    6.1             3.0            4.6             1.4            1         3.2857142857142856  2.033333333333333   -1.445047446393471   -0.1019091578746504   -0.01899012239493801  0.020980767646090408  0.1614215276667148    -0.02716639637934938   1\n",
       "2    6.6             2.9            4.6             1.3            1         3.538461538461538   2.2758620689655173  -1.330564613235537   -0.41978474749131267  0.1759590589290671    -0.4631301992308477   0.08304243689815374   -0.033351733677429274  1\n",
       "3    6.7             3.3            5.7             2.1            2         2.7142857142857144  2.0303030303030303  -2.6719170661531013  -0.9149428897499291   0.4156162725009377    0.34633692661436644   0.03742964707590906   -0.013254286196245774  2\n",
       "4    5.5             4.2            1.4             0.2            0         6.999999999999999   1.3095238095238095  3.6322930267831404   0.8198526437905096    1.046277579362938     0.09738737839850209   0.09412658096734221   0.1329137026697501     0\n",
       "...  ...             ...            ...             ...            ...       ...                 ...                 ...                  ...                   ...                   ...                   ...                   ...                    ...\n",
       "25   5.5             2.5            4.0             1.3            1         3.0769230769230766  2.2                 -1.2523120088600896  0.5975071562677784    -0.7019801415469216   -0.11489031841855571  -0.03615945782087869  0.005496321827264977   1\n",
       "26   5.8             2.7            3.9             1.2            1         3.25                2.148148148148148   -1.0792352165904657  0.5236883751378523    -0.34037717939532286  -0.23743695029955128  -0.00936891422024664  -0.02184110533380834   1\n",
       "27   4.4             2.9            1.4             0.2            0         6.999999999999999   1.517241379310345   3.7422969192506095   1.048460304741977     -0.636475521315278    0.07623157913054074   0.004215355833312173  -0.06354157393133958   0\n",
       "28   4.5             2.3            1.3             0.3            0         4.333333333333334   1.956521739130435   1.4537380535696471   2.4197864889383505    -1.0301500321688102   -0.5150263062576134   -0.2631218962099228   -0.06608059456656257   0\n",
       "29   6.9             3.2            5.7             2.3            2         2.4782608695652177  2.15625             -2.963110301521378   -0.924626055589704    0.44833006106219797   0.20994670504662372   -0.2012725506779131   -0.018900414287719353  2"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = df_train.state_get()\n",
    "\n",
    "df_test.state_set(state)\n",
    "df_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And just like that `df_test` contains all the columns, transformations and the prediction we modelled on the training set. The state can be easily serialized to disk in a form of a JSON file. This makes deployment of a machine learning model as trivial as simply copying a JSON file from one environment to another."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-06-04T12:37:48.253674Z",
     "start_time": "2020-06-04T12:37:48.198218Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<thead>\n",
       "<tr><th>#                             </th><th>sepal_length  </th><th>sepal_width  </th><th>petal_length  </th><th>petal_width  </th><th>class_  </th><th>petal_ratio       </th><th>sepal_ratio       </th><th>PCA_0              </th><th>PCA_1               </th><th>PCA_2               </th><th>PCA_3               </th><th>PCA_4               </th><th>PCA_5                </th><th>prediction  </th></tr>\n",
       "</thead>\n",
       "<tbody>\n",
       "<tr><td><i style='opacity: 0.6'>0</i> </td><td>5.9           </td><td>3.0          </td><td>4.2           </td><td>1.5          </td><td>1       </td><td>2.8000000000000003</td><td>1.9666666666666668</td><td>-1.642627940409072 </td><td>0.49931302910747727 </td><td>-0.06308800806664466</td><td>0.10842057110641677 </td><td>-0.03924298664189224</td><td>-0.027394439700272822</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>1</i> </td><td>6.1           </td><td>3.0          </td><td>4.6           </td><td>1.4          </td><td>1       </td><td>3.2857142857142856</td><td>2.033333333333333 </td><td>-1.445047446393471 </td><td>-0.1019091578746504 </td><td>-0.01899012239493801</td><td>0.020980767646090408</td><td>0.1614215276667148  </td><td>-0.02716639637934938 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>2</i> </td><td>6.6           </td><td>2.9          </td><td>4.6           </td><td>1.3          </td><td>1       </td><td>3.538461538461538 </td><td>2.2758620689655173</td><td>-1.330564613235537 </td><td>-0.41978474749131267</td><td>0.1759590589290671  </td><td>-0.4631301992308477 </td><td>0.08304243689815374 </td><td>-0.033351733677429274</td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>3</i> </td><td>6.7           </td><td>3.3          </td><td>5.7           </td><td>2.1          </td><td>2       </td><td>2.7142857142857144</td><td>2.0303030303030303</td><td>-2.6719170661531013</td><td>-0.9149428897499291 </td><td>0.4156162725009377  </td><td>0.34633692661436644 </td><td>0.03742964707590906 </td><td>-0.013254286196245774</td><td>2           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>4</i> </td><td>5.5           </td><td>4.2          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.3095238095238095</td><td>3.6322930267831404 </td><td>0.8198526437905096  </td><td>1.046277579362938   </td><td>0.09738737839850209 </td><td>0.09412658096734221 </td><td>0.1329137026697501   </td><td>0           </td></tr>\n",
       "<tr><td>...                           </td><td>...           </td><td>...          </td><td>...           </td><td>...          </td><td>...     </td><td>...               </td><td>...               </td><td>...                </td><td>...                 </td><td>...                 </td><td>...                 </td><td>...                 </td><td>...                  </td><td>...         </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>25</i></td><td>5.5           </td><td>2.5          </td><td>4.0           </td><td>1.3          </td><td>1       </td><td>3.0769230769230766</td><td>2.2               </td><td>-1.2523120088600896</td><td>0.5975071562677784  </td><td>-0.7019801415469216 </td><td>-0.11489031841855571</td><td>-0.03615945782087869</td><td>0.005496321827264977 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>26</i></td><td>5.8           </td><td>2.7          </td><td>3.9           </td><td>1.2          </td><td>1       </td><td>3.25              </td><td>2.148148148148148 </td><td>-1.0792352165904657</td><td>0.5236883751378523  </td><td>-0.34037717939532286</td><td>-0.23743695029955128</td><td>-0.00936891422024664</td><td>-0.02184110533380834 </td><td>1           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>27</i></td><td>4.4           </td><td>2.9          </td><td>1.4           </td><td>0.2          </td><td>0       </td><td>6.999999999999999 </td><td>1.517241379310345 </td><td>3.7422969192506095 </td><td>1.048460304741977   </td><td>-0.636475521315278  </td><td>0.07623157913054074 </td><td>0.004215355833312173</td><td>-0.06354157393133958 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>28</i></td><td>4.5           </td><td>2.3          </td><td>1.3           </td><td>0.3          </td><td>0       </td><td>4.333333333333334 </td><td>1.956521739130435 </td><td>1.4537380535696471 </td><td>2.4197864889383505  </td><td>-1.0301500321688102 </td><td>-0.5150263062576134 </td><td>-0.2631218962099228 </td><td>-0.06608059456656257 </td><td>0           </td></tr>\n",
       "<tr><td><i style='opacity: 0.6'>29</i></td><td>6.9           </td><td>3.2          </td><td>5.7           </td><td>2.3          </td><td>2       </td><td>2.4782608695652177</td><td>2.15625           </td><td>-2.963110301521378 </td><td>-0.924626055589704  </td><td>0.44833006106219797 </td><td>0.20994670504662372 </td><td>-0.2012725506779131 </td><td>-0.018900414287719353</td><td>2           </td></tr>\n",
       "</tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "#    sepal_length    sepal_width    petal_length    petal_width    class_    petal_ratio         sepal_ratio         PCA_0                PCA_1                 PCA_2                 PCA_3                 PCA_4                 PCA_5                  prediction\n",
       "0    5.9             3.0            4.2             1.5            1         2.8000000000000003  1.9666666666666668  -1.642627940409072   0.49931302910747727   -0.06308800806664466  0.10842057110641677   -0.03924298664189224  -0.027394439700272822  1\n",
       "1    6.1             3.0            4.6             1.4            1         3.2857142857142856  2.033333333333333   -1.445047446393471   -0.1019091578746504   -0.01899012239493801  0.020980767646090408  0.1614215276667148    -0.02716639637934938   1\n",
       "2    6.6             2.9            4.6             1.3            1         3.538461538461538   2.2758620689655173  -1.330564613235537   -0.41978474749131267  0.1759590589290671    -0.4631301992308477   0.08304243689815374   -0.033351733677429274  1\n",
       "3    6.7             3.3            5.7             2.1            2         2.7142857142857144  2.0303030303030303  -2.6719170661531013  -0.9149428897499291   0.4156162725009377    0.34633692661436644   0.03742964707590906   -0.013254286196245774  2\n",
       "4    5.5             4.2            1.4             0.2            0         6.999999999999999   1.3095238095238095  3.6322930267831404   0.8198526437905096    1.046277579362938     0.09738737839850209   0.09412658096734221   0.1329137026697501     0\n",
       "...  ...             ...            ...             ...            ...       ...                 ...                 ...                  ...                   ...                   ...                   ...                   ...                    ...\n",
       "25   5.5             2.5            4.0             1.3            1         3.0769230769230766  2.2                 -1.2523120088600896  0.5975071562677784    -0.7019801415469216   -0.11489031841855571  -0.03615945782087869  0.005496321827264977   1\n",
       "26   5.8             2.7            3.9             1.2            1         3.25                2.148148148148148   -1.0792352165904657  0.5236883751378523    -0.34037717939532286  -0.23743695029955128  -0.00936891422024664  -0.02184110533380834   1\n",
       "27   4.4             2.9            1.4             0.2            0         6.999999999999999   1.517241379310345   3.7422969192506095   1.048460304741977     -0.636475521315278    0.07623157913054074   0.004215355833312173  -0.06354157393133958   0\n",
       "28   4.5             2.3            1.3             0.3            0         4.333333333333334   1.956521739130435   1.4537380535696471   2.4197864889383505    -1.0301500321688102   -0.5150263062576134   -0.2631218962099228   -0.06608059456656257   0\n",
       "29   6.9             3.2            5.7             2.3            2         2.4782608695652177  2.15625             -2.963110301521378   -0.924626055589704    0.44833006106219797   0.20994670504662372   -0.2012725506779131   -0.018900414287719353  2"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_train.state_write('./iris_model.json')\n",
    "\n",
    "df_test.state_load('./iris_model.json')\n",
    "df_test"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
